package server;

import java.util.Random;
import java.util.Vector;

import shared.Vec2i;
import vmo.Chunk;
import vmo.Tile;
import vmo.Tile.TileType;

public class Generators {

	static Random g_random;
	
	public static void Initialize()
	{
		g_random=new Random();
		
	}
	
	public static Chunk [][] WorldGenerator0(short width, short height)
	{
		Chunk [][] grid=new Chunk[width][];
		
		for (int i=0;i<width;i++)
		{
			grid[i]=new Chunk[height];
			for (int j=0;j<height;j++)
			{
				grid[i][j]=new Chunk(true);
				grid[i][j].m_position=new Vec2i(i,j);
			}
		}
		
		
		
		//build a height map
		float [][] map=Heightmap(width*16, height*16,16 );
		map=Soften(map,width*16,height*16);
		map=Soften(map,width*16,height*16);
		//fill in
		
		//fill in with soil
		for (int i=0;i<width;i++)
		{
			for (int j=0;j<height;j++)
			{
				for (int k=0;k<16;k++)
				{
					for (int l=0;l<16;l++)
					{
						for (int depth=0;depth<16;depth++)
						{
							if (depth<map[(i*16)+k][(j*16)+l])
							{
								//build solid tile
								if (depth+2<map[(i*16)+k][(j*16)+l])
								{
									grid[i][j].m_tiles[depth][k][l]=new Tile(TileType.WALL,(short)1);					
								}
								else
								{
									grid[i][j].m_tiles[depth][k][l]=new Tile(TileType.WALL,(short)0);
								}	
							}
							else
							{
								if (depth<10)
								{
									grid[i][j].m_tiles[depth][k][l]=new Tile(TileType.FLUID,(short)0);				
								}
								else
								{
									if (depth<1+map[(i*16)+k][(j*16)+l])
									{
										if ((i*16)+k>0 && (i*16)+k<(width*16)-1 && (j*16)+l>0 && (j*16)+l<(height*16)-1)
										{
											int r=RampLocation(map,(i*16)+k,(j*16)+l,(int) map[(i*16)+k][(j*16)+l]);
											if (r>-1)
											{
												grid[i][j].m_tiles[depth][k][l]=new Tile(TileType.RAMP,(short)0);
												grid[i][j].m_tiles[depth][k][l].setFacing((byte) r);
											}
											else
											{
												grid[i][j].m_tiles[depth][k][l]=new Tile(TileType.FLOOR,(short)0);		
											}
										}
										else
										{
											grid[i][j].m_tiles[depth][k][l]=new Tile(TileType.FLOOR,(short)0);								
										}
									}
									else
									{
										//build empty tile
										grid[i][j].m_tiles[depth][k][l]=new Tile(TileType.VOID,(short)0);	
									}
								}
							}
							grid[i][j].m_tiles[depth][k][l].m_widget=Byte.MIN_VALUE;
						}
					}
				}
				
			}
		}
	
		Trees(grid, map, width*16, height*16, 16, 16);
		
		return grid;
	}
	
	static int RampLocation(float [][] map, int x, int y, int z)
	{
		int [] eight=new int[8];
		eight[0]=(int) map[x-1][y];
		eight[1]=(int) map[x+1][y];
		eight[2]=(int) map[x][y-1];
		eight[3]=(int) map[x][y+1];	
		eight[4]=(int) map[x-1][y-1];
		eight[5]=(int) map[x+1][y+1];
		eight[6]=(int) map[x+1][y-1];
		eight[7]=(int) map[x-1][y+1];			
		for (int i=0;i<8;i++)
		{
			if (eight[i]>z && eight[i]<z+2)
			{
				if (eight[AltRamp(i)]==z)
				{
					return i;
				}
			}
		}		
		return -1;
	}

	static int AltRamp(int i)
	{
		switch (i)
		{
		case 0:
			return 1;
		case 1:
			return 0;		
		case 2:
			return 3;		
		case 3:
			return 2;	
		case 4:
			return 5;
		case 5:
			return 4;
		case 6:
			return 7;
		case 7:
			return 6;	
		}
		return 0;
	}
	
	static float [][] Soften(float [][] map, int width, int height)
	{
		for (int i=1;i<width-1;i++)
		{
			for (int j=1;j<height-1;j++)
			{
				float v=map[i-1][j]+map[i][j]+map[i+1][j];
				v+=map[i][j-1]+map[i][j+1];
				v=v/5;
				map[i][j]=v;
			}
		}
		return map;
	}
	
	static float [][] Heightmap(int width, int height, float noise)
	{
		float [][]heights=new float[width][];
		for (int i=0;i<width;i++)
		{
			heights[i]=new float[height];
			for (int j=0;j<height;j++)
			{
				heights[i][j]=10;
			}
			
		}
		
		int degrees=1;
		int w=width;
		while (w>1)
		{
			degrees++;
			w=w>>1;
		}
		//begin subdivision
		heights=Midpoint_displacement(heights,0,0,width-1,height-1,noise,degrees);

		for (int i=0;i<width;i++)
		{
			for (int j=0;j<height;j++)
			{
				if (heights[i][j]>14)
				{
					heights[i][j]=14;
				}
				if (heights[i][j]<6)
				{
					heights[i][j]=6;
				}
			}
		}
		
		return heights;	
	}
	
	static void Trees(Chunk [][] grid, float [][] heightmap, int width, int height, int spread, int proliferation)
	{		
		
		Vector <Vec2i> m_forests=new Vector <Vec2i>();
		for (int i=0;i<proliferation;i++)
		{
			//pick a random spot at altitude less than 12 that's more than proliferation away from another forest
			Vec2i p=null; 
			boolean valid=true;
			p=new Vec2i(g_random.nextInt(width-(spread*2))+spread,g_random.nextInt(height-(spread*2))+spread);
			while (!valid)
			{
				p=new Vec2i(g_random.nextInt(width-(spread*2))+spread-1,g_random.nextInt(height-(spread*2))+spread-1);
				if (heightmap[p.x][p.y]<10 || heightmap[p.x][p.y]>13)
				{
					valid=false;
				}
					for (int j=0;j<m_forests.size();j++)
					{
						float x=Math.abs(p.x-m_forests.get(j).x);
						float y=Math.abs(p.y-m_forests.get(j).y);
						x=x*x; y=y*y;
						double d=Math.sqrt(x+y);
						if (d<spread*2)
						{
							valid=false;
						}
						
					}					
			}
			
			for (int j=p.x;j<spread+p.x;j+=3)
			{
				for (int k=p.y;k<spread+p.y;k+=3)
				{
					int x=j-2+g_random.nextInt(5);
					int y=k-2+g_random.nextInt(5);
					for (int l=15;l>0;l--)
					{
						if (grid[x/16][y/16].m_tiles[l][x%16][y%16].m_type==TileType.FLOOR)
						{
							if (g_random.nextBoolean())
							{
								grid[x/16][y/16].m_tiles[l][x%16][y%16].m_widget=Byte.MIN_VALUE+3;		
							}
							else
							{
								grid[x/16][y/16].m_tiles[l][x%16][y%16].m_widget=Byte.MIN_VALUE+1;
								grid[x/16][y/16].m_tiles[l+1][x%16][y%16].m_widget=Byte.MIN_VALUE+2;
							}
							break;
						}
					}
					
				}
			}		
			m_forests.add(p);
		}
		
	}
	
	static float [][] Midpoint_displacement(float [][] heightmap,int x, int y, int width, int height, float noise,int iterations)
	{
		//diamond phase
		heightmap[x+width/2][y]=(heightmap[x][y]+heightmap[x+width][y])/2;
		heightmap[x+width/2][y+height]=(heightmap[x][y+height]+heightmap[x+width][y+height])/2;
		heightmap[x][y+height/2]=(heightmap[x][y]+heightmap[x][y+height])/2;
		heightmap[x+width][y+height/2]=(heightmap[x+width][y]+heightmap[x+width][y+height])/2;
		
		float v=(heightmap[x][y]+heightmap[x+width][y]+heightmap[x+width][y+height]+heightmap[x][y+height])/4;
		float n=noise;
		v=v-(n/2);
		v=v+g_random.nextFloat()*n;
		heightmap[x+width/2][y+height/2]=v;
		if (iterations>0)
		{
			heightmap=Midpoint_displacement(heightmap,x,y,width/2,height/2,noise-1,iterations-1);
			heightmap=Midpoint_displacement(heightmap,x+width/2,y,width/2,height/2,noise-1,iterations-1);			
			heightmap=Midpoint_displacement(heightmap,x+width/2,y+height/2,width/2,height/2,noise-1,iterations-1);	
			heightmap=Midpoint_displacement(heightmap,x,y+height/2,width/2,height/2,noise-1,iterations-1);			
		}	
		else
		{
			char test='b';
		}
		return heightmap;
	}
}
